package com.rgs.module.login.service.impl;

import com.google.common.base.Strings;
import com.rgs.module.login.bo.LoginUserBO;
import com.rgs.module.login.constant.LoginConstants;
import com.rgs.module.login.service.LoginCacheService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Ren Gaoshuai
 * @ClassName LoginCacheServiceImpl
 * @date 2021-12-24 16:29
 **/
@Service
public class LoginCacheServiceImpl implements LoginCacheService {

    private static final Integer DEF_EXPIRE_EXPIRE = 2;
    //请求头header中的Authorization字段
    private static final String HEADER = "Authorization";

    //配置文件中的随机秘钥
    @Value("${rgs.security.secret}")
    private String secret;

    @Resource
    RedisTemplate redisTemplate;

    /*--------验证码相关----start--*/

    /**
     * 根据uuid保存验证码
     * @param uuid 唯一标识
     * @param code 验证码值
     */
    @Override
    public void captchaPut(String uuid, String code) {
        String key = this.getCaptchaKey(uuid);
        redisTemplate.opsForValue().set(key, code, 2, TimeUnit.MINUTES);
    }

    /**
     * 校验验证码
     * @param uuid 唯一标识
     * @return
     */
    private String getCaptchaKey(String uuid) {
        return LoginConstants.CAPTCHA_CODE_KEY + uuid;
    }

    @Override
    public Boolean checkCaptcha(String uuid, String code) {
        String verifyKey = this.getCaptchaKey(uuid);
        String captcha = (String) redisTemplate.opsForValue().get(verifyKey);
        if (Strings.isNullOrEmpty(captcha)) {
            return false;
        }
        if (!code.equalsIgnoreCase(captcha)) {
            return false;
        }
        return true;
    }

    /*--------验证码相关----end--*/


    /*--------用户token相关----start--*/

    @Override
    public void verifyToken(LoginUserBO loginUser) {
        refreshToken(loginUser);
    }

    /**
     * 刷新token
     * @param loginUser 登录信息
     */
    @Override
    public void refreshToken(LoginUserBO loginUser) {
        loginUser.setLoginDateTime(LocalDateTime.now());
        //根据用户uuid 获取rediskey
        String userRedisKey = getTokenKey(loginUser.getUserId());
        redisTemplate.opsForValue().set(userRedisKey,loginUser,DEF_EXPIRE_EXPIRE,TimeUnit.HOURS);
    }

    private String getTokenKey(String uuid){
        return LoginConstants.LOGIN_TOKEN_KEY.concat(uuid);
    }

    /**
     * 创建token
     * @param loginUser 用户信息
     * @return
     */
    @Override
    public String createToken(LoginUserBO loginUser) {
        String token = loginUser.getUserId();
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(LoginConstants.LOGIN_USER_KEY,token);

        return createToken(claims);
    }

    /**
     * 真正创建token
     */
    public String createToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();
    }

    /**
     * 解析请求中的token信息
     * @param request
     * @return
     */
    @Override
    public LoginUserBO getLoginUser(HttpServletRequest request) {
        //拿到token
        String token = getToken(request);
        if (!Strings.isNullOrEmpty(token)){
            //拿到uuid
            Claims claims = parseToken(token);
            // 解析对应的权限以及用户信息
            String uuid = (String) claims.get(LoginConstants.LOGIN_USER_KEY);
            String userKey = getTokenKey(uuid);
            LoginUserBO user =
                    (LoginUserBO) redisTemplate.opsForValue().get(userKey);
            return user;
        }
        return null;
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(HttpServletRequest request) {
        String token = request.getHeader(HEADER);
        if (!Strings.isNullOrEmpty(token) &&
                token.startsWith(LoginConstants.TOKEN_PREFIX)) {
            token = token.replace(LoginConstants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    @Override
    public void delLoginUser(String token) {
        if (!Strings.isNullOrEmpty(token)) {
            String userKey = getTokenKey(token);
            redisTemplate.delete(userKey);
        }
    }

    /*--------用户token相关----end--*/
}
